﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SqlServerDriver
{
/*
	The View class of a model/view/controller pattern
	The goal is that the view class contains no logic that acts on
	the model, delegatating all operations to the controller.
	The controller requires a few methods to refresh controls as
	the binding mechanism does not work perfectly for every case.
*/
	internal partial class SelectObjectListView : Form
	{
		// > Boilerplate. The view needs a controller reference in order to delegate actual work
		SelectObjectListController controller;

		// > Boilerplate: construct with a controller. The controller holds the model to be acted on.
		internal SelectObjectListView(SelectObjectListController controllerIn, String objectTypeText)
		{
			InitializeComponent();
			this.controller = controllerIn;
			// register self as the view with the controller
			this.controller.View = this;
			// set caption
			this.Text = this.Text.Replace("$O$", objectTypeText);
			// set up bindings of control properties to properties in the model or controller
			InitialiseBindings();
		}
		// < Boilerplate

		////////////////////////////////////////////////////////////////////////////////////////////
		// API for controller
		
		// Update the left pick list when initialising, adding or deleting
		public void RefreshSource(List<string> source)
		{
			// this.listLeft.DataSource = source;
			this.RefreshSource(listLeft, source, null);
			this.listLeft.DataBindings[0].ReadValue();
		}

		// Update the right pick list when initialising, adding or deleting
		public void RefreshSelected(List<string> source)
		{
			this.RefreshSource(listRight, source, null);
			this.listRight.DataBindings[0].ReadValue();
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		// Helper for the above
		private void RefreshSource(ListControl ctrlList, object source, string displayPropertyName)
		{
			// if the source is the same as what is sent in then undo it to 
			// force a refresh
			if (ctrlList.DataSource == source)
				ctrlList.DataSource = null;
			ctrlList.DataSource = source;
			if (!string.IsNullOrEmpty(displayPropertyName))
				ctrlList.DisplayMember = displayPropertyName;
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		// Respond to events but delegating to the controller
		// note the single line layout to denote these are expected to contain a single statement

		private void ListSampleView_Load(object sender, EventArgs e) { this.controller.SetFormInitialState(); }
		private void btnClose_Click(object sender, EventArgs e) { OnClose(); }
		private void btnCancel_Click(object sender, EventArgs e) { OnCancel(); }
		private void btnAdd_Click(object sender, EventArgs e) { OnAdd(false); }					// false as model refresh is not required.
		private void btnDel_Click(object sender, EventArgs e) { this.controller.OnDelete(); }
		private void btnAddAll_Click(object sender, EventArgs e) { OnAddAll(false); }
		private void btnDellAll_Click(object sender, EventArgs e) { OnDelAll(false); }

		// private void btnDelAll_Click(object sender, EventArgs e) { this.controller.OnDelete(); }
		private void listLeft_MouseDoubleClick(object sender, MouseEventArgs e) { OnAdd(true); }  // true as model refresh is required
		private void btnClear_Click(object sender, EventArgs e) { this.textFilter.Text = ""; controller.ClearFilter(); }

		///////////////////////////////////////////////////////////////////////////////////////
		// Common methods specialised for the current model
		private void InitialiseBindings()
		{
			// bind: 
			this.listLeft.DataBindings.Add("SelectedItem", this.controller, "SourceSelection", false, DataSourceUpdateMode.OnPropertyChanged);
			this.listRight.DataBindings.Add("SelectedItem", this.controller, "SelectedSelection", false, DataSourceUpdateMode.OnPropertyChanged);
			this.textFilter.DataBindings.Add("Text", this.controller, "Filter", false, DataSourceUpdateMode.OnPropertyChanged);
		}

		///////////////////////////////////////////////////////////////////////////////////////////
		// Boilerplate methods
		// 

		private void OnClose() // ie Commit the changes to the model
		{
			/*
				if (!this.controller.CommitChangesToModel())
				{
					MessageBox(this.Controller.GetLastError(), "Cannot close", ....
				}
				else
				{
					this.DialogResult = System.Windows.Forms.DialogResult.OK;
					this.Close();
				}
			*/
			// possibly include validation as above
			this.controller.CommitChangesToModel();
			this.DialogResult = System.Windows.Forms.DialogResult.OK;
			this.Close();
		}

		private void OnCancel()
		{
			bool closeIt = true;
			if (this.controller.IsDirty())
			{
				String message = "Changes made. Do you wish to discard them?";
				DialogResult answer = MessageBox.Show(message, "Discard edits", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				closeIt = (answer == DialogResult.Yes);
			}
			if (closeIt)
			{
				this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
				this.Close();
			}
		}

		void OnAdd(bool updateModel)
		{
			// Update the model if processing a double-click in the control
			// as the model is only updated by the framework when the control loses focus.
			if (updateModel)
				this.listLeft.DataBindings[0].WriteValue();
			if (this.listLeft.SelectedItems.Count > 0)
			{
				foreach (object o in this.listLeft.SelectedItems)
					this.controller.OnAddOne(o.ToString());
				// leave with only one item selected
				this.listLeft.SelectedItems.Clear();
				if (this.listLeft.Items.Count > 0)
					this.listLeft.SelectedIndex = 0;
			}
			else
				this.controller.OnAdd();
		}
		
		private void OnAddAll(bool p)
		{
			foreach (object o in this.listLeft.Items)
				this.controller.OnAddOne(o.ToString());
		}

		private void OnDelAll(bool p)
		{
			// two stages or .Net throws modified enumerator exception
			List<string> itemsToRemove = new List<string>();
			foreach (object o in this.listRight.Items)
				itemsToRemove.Add(o.ToString());
			foreach (string item in itemsToRemove)
				this.controller.OnDeleteOne(item);
		}

		private void textFilter_TextChanged(object sender, EventArgs e)
		{
			TextBox t = sender as TextBox;
			// Because focus not lost as text is entered.
			if (t != null)
				t.DataBindings[0].WriteValue();
			this.controller.OnFilterChanged();
		}
	}
}
